#include "http_connect.h"
#include "http_state.h"
#include "logger/logger.h"
#include <unistd.h>

namespace zh
{
    std::string HttpConnection::s_sourceDirectory;
    bool        HttpConnection::s_isEdgeTrigger;

    void HttpConnection::initialize(int socketFD, const sockaddr_in& address)
    {
        m_socketFD = socketFD;
        m_address  = address;
        m_isClose  = false;

        m_readBuffer.initialize();
        m_writeBuffer.initialize();
    }

    void HttpConnection::clear()
    {
        close();
    }

    void HttpConnection::close()
    {
        m_response.unmapFile();
        if (!m_isClose)
        {
            ::close(m_socketFD);
            m_isClose = true;
        }
    }

    bool HttpConnection::handleConnection()
    {
        m_request.initialize();
        if (m_readBuffer.readableBytes() <= 0)
        {
            return false;
        }
        // LOG_INFO("handleConnection ===== ");
        if (m_request.parse(m_readBuffer))
        {
            // LOG_INFO("m_request.parse(m_readBuffer) done");
            m_response.initialize(s_sourceDirectory, m_request.getPath(), m_request.isKeepAlive(), HttpStateCode::OK);
        }
        else
        {
            m_response.initialize(s_sourceDirectory, m_request.getPath(), false, HttpStateCode::BAD_REQUEST);
        }
        m_response.makeResponse(m_writeBuffer);
        // response header
        m_iov[0].iov_base = const_cast<char*>(m_writeBuffer.curReadPtr());
        m_iov[0].iov_len  = m_writeBuffer.readableBytes();
        m_iovCount        = 1;
        // response file
        if (m_response.fileLength() > 0 && m_response.file() != nullptr)
        {
            m_iov[1].iov_base = m_response.file();
            m_iov[1].iov_len  = m_response.fileLength();
            m_iovCount        = 2;
        }
        // LOG_INFO("m_iov[0].iov_len :{}, count: {}", m_iov[0].iov_len, m_iovCount);
        // LOG_INFO("write buffer: {}", m_writeBuffer.toString());
        return true;
    }

    ssize_t HttpConnection::readBuffer(int& savEerroNo)
    {
        ssize_t length = -1;
        do
        {
            length = m_readBuffer.readFD(m_socketFD, &savEerroNo);
            if (length <= 0)
            {
                break;
            }
        } while (s_isEdgeTrigger);
        return length;
    }

    ssize_t HttpConnection::writeBuffer(int& savEerroNo)
    {
        ssize_t length = -1;
        do
        {
            length = writev(m_socketFD, m_iov, m_iovCount);
            // LOG_INFO("# writeBuffer length:{} ip: {}, port:{}", length, getIP(), getPort());
            if (length <= 0)
            {
                savEerroNo = errno;
                break;
            }
            auto transmitEnd = [&]() -> bool { return m_iov[0].iov_len + m_iov[1].iov_len == 0; };
            if (transmitEnd())
            {
                // LOG_INFO("transmitEnd");
                break;
            }
            else if (length > m_iov[0].iov_len)
            {
                // LOG_INFO("m_iov[0].iov_len: {}", m_iov[0].iov_len);
                m_iov[1].iov_base = static_cast<uint8_t*>(m_iov[1].iov_base) + (length - m_iov[0].iov_len);
                m_iov[1].iov_len -= (length - m_iov[0].iov_len);
                if (m_iov[0].iov_len > 0)
                {
                    m_writeBuffer.initialize();
                    m_iov[0].iov_len = 0;
                }
            }
            else
            {
                // LOG_INFO("=====  m_writeBuffer.updateReadPtr(length)");
                m_iov[0].iov_base = static_cast<uint8_t*>(m_iov[0].iov_base) + length;
                m_iov[0].iov_len -= length;
                m_writeBuffer.updateReadPtr(length);
            }
        } while (s_isEdgeTrigger || getWriteBytes() > 10240);
        return length;
    }

    const char* HttpConnection::getIP() const
    {
        return inet_ntoa(m_address.sin_addr);
    }

    int HttpConnection::getPort() const
    {
        return m_address.sin_port;
    }

    int HttpConnection::getFD() const
    {
        return m_socketFD;
    }

    int HttpConnection::getWriteBytes() const
    {
        return m_iov[0].iov_len + m_iov[1].iov_len;
    }

    bool HttpConnection::isKeepAlive() const
    {
        return m_request.isKeepAlive();
    }
} // namespace zh